home *** CD-ROM | disk | FTP | other *** search
/ Beginning Mac Programming / Beginning Mac Programming.bin / Open Me for REALbasic 3 / REALbasic 3.2 / Example Projects / Applications / ListsInFiles / FileListMethods.ST < prev    next >
Text File  |  2001-01-05  |  10KB  |  209 lines

  1. FileListMethods:
  2.  
  3. A package for building and manipulating linked-list structures in REALbasic binary files
  4.  
  5. Why do this?
  6.  
  7. Many applications can benefit from treating the data fork of a file as a binary stream. We can have random access to data within it and are not constrained by the necessity of sequential reading and writing of a large amount of information in order to read or write just a portion of it. For applications that require frequent changes to the stored data, it is possible to implement storage management so that space in the file that is no longer required can be reused for new data.
  8.  
  9. This package implements linked-list capabilities in REALbasic binary streams. Initially carried out as an experiment to find out if performance would be acceptable, it has been applied successfully in two applications -- the first an accounting program that uses fixed-size blocks to store a large number of individual transaction items and the second a text-editing program that needs to store variable-length text data as well as formatting information.
  10.  
  11. Data types
  12.  
  13. The basic element of storage used by this package is fixed-size blocks of any size you choose to define. These are linked by forward and backward pointers into lists of arbitrary length. A list can include blocks of any of the defined sizes.
  14.  
  15. Each block has the following fields in order:
  16.  
  17.    a forward pointer (a 4-byte integer interpreted as a byte offset within the file)
  18.    a reverse pointer (as above)
  19.    a 4-byte integer giving the size of the data field in bytes
  20.    a data field of the defined size
  21.  
  22. A pointer to a block is the byte address (0... ) of the block's data field i.e. the forward and back pointers and the size field begin 12 bytes earlier in the file.
  23.  
  24. Lists start from list heads which are stored at byte offsets of 8, 12, 16, ... from the start of the file. Within the lists the blocks are linked by both forward and backward pointers. The null pointer has the value 0.
  25.  
  26. You are free to use the data field of a block in any way e.g. starting from a particular byte position within it, read or write integers, doubles, ASCII text, pointers to other lists etc.
  27.  
  28. For each block size there is a list head to attach a list of free blocks so that they can be reused. When a request is made to allocate a new block of a particular size, the free list for that size is searched first, before the overall file size is increased. If a block cannot be obtained from the free list, it is allocated at the end of the file whose size is thereby increased.
  29.  
  30. Methods are included for writing and reading text strings of arbitrary length, using linked blocks as the basic storage technique. It is possible for these strings can be built up from a sequence of arbitrary-length tokens before recording in the file, and then separated into their constituents on reading.
  31.  
  32. Methods for general list administration
  33.  
  34. initDB(nLists as integer, nTypes as integer)
  35.  
  36.    Declares the structure to be created with a potential for data lists 0...(nList - 1)
  37.    based at byte offsets 8, 12, 16, ... in the binary file, and 'nTypes' different block 
  38.    sizes.
  39.    
  40. declareBlock(type as integer, size as integer)
  41.  
  42.    Declares a block type 'type' (0... ) having 'size' bytes for data in addition 
  43.    to forward and backward pointers and an integer field specifying the data field 
  44.    size. (At present the number of different sizes is limited to 16 by a fixed-size
  45.    array 'bSizes' used to store the data size of the different block types. There
  46.    is no good reason for doing it this way and it can easily be changed.)
  47.    
  48. newBlock(bs as binaryStream, type as integer) as integer
  49.  
  50.    Allocates a new block in the stream 'bs' of type 'type' and returns a pointer to 
  51.    its data field. 
  52.    
  53. addToList(bs as binaryStream, blk as integer, n as integer)
  54.  
  55.    Links the block 'blk' at the start of data list 'n', in stream 'bs'. The list is 
  56.    doubly-linked i.e. has both forward and backward pointers.
  57.    
  58. deleteFromList(bs as binaryStream, blk as integer, n as integer)
  59.  
  60.    Deletes the block referenced by 'blk' from data list 'n' in stream 'bs'.
  61.    
  62. firstBlock(bs as binaryStream, dataList as integer) as integer
  63.  
  64.    Returns a pointer to the first block of the list 'dataList' (0... ), or 0 if that 
  65.    list is empty, in stream 'bs'.
  66.    
  67. nextBlock(bs as binaryStream, blk as integer) as integer
  68.  
  69.    Given 'blk' as the pointer to a block, returns a pointer to the next block in the 
  70.    list in stream 'bs', or 0 if it is the last one.
  71.    
  72. prevBlock(bs as binaryStream, blk as integer) as integer
  73.  
  74.    Given 'blk' as the pointer to a block, returns a pointer to the previous block in 
  75.    the list in stream 'bs', or 0 if it is the first one.
  76.    
  77. insertPre(bs as binaryStream, blk as integer, p as integer)
  78.  
  79.    Inserts the block pointed to by 'blk' in stream 'bs' before the block pointed to 
  80.    by 'p'.
  81.    
  82. insertPost(bs as binaryStream, blk as integer, p as integer)
  83.  
  84.    Inserts the block pointed to by 'blk' in stream 'bs' after the block pointed to 
  85.    by 'p'.
  86.    
  87. freeList(bs as binaryStream, blk as integer)
  88.  
  89.    Returns to free storage the list pointed to by 'blk' in binaryStream 'bs'. If 'blk' 
  90.    is 0, then nothing happens. The list may contain blocks of mixed types.
  91.    
  92. freeBlock(bs as binaryStream, blk as integer)
  93.  
  94.    Determines which free list the block referenced by 'blk' in stream 'bs' belongs to,
  95.    and adds it to that list. (N.B. there is a separate free list for each block size,
  96.    and the list heads for these are immediately after the list heads for data lists 
  97.    0... )
  98.    
  99. nFreeBlocks(bs as binaryStream, type as integer) as integer
  100.  
  101.    Returns the number of free blocks of type 'type' (0... 16) in stream 'bs'.
  102.    
  103. openNewFile(f as folderItem, byref bs as binaryStream) as boolean
  104.  
  105.    Creates a file as folderItem 'f'and opens it as a binaryStream 'bs'. If successful, 
  106.    returns true. (N.B. there is a place provided to call your choice of error alert 
  107.    if an exception occurs in this process.)
  108.    
  109. openFile(f as folderItem, bs as binaryStream) as boolean
  110.  
  111.    Attempts to open folderItem 'f' as binaryStream 'bs', returning true if successful.
  112.    
  113. Recording arbitrary text strings in list form 
  114.  
  115. declareTextList(blkType as integer)
  116.  
  117.    Declares that text lists will be created and read using blocks of type 'blkType'
  118.    (0... ).
  119.    
  120. textToList(bs as binaryStream, txt as string) as integer
  121.  
  122.    Stores 'txt' as a linked list of blocks in stream 'bs' and returns a pointer to that 
  123.    list. The text string is stored in the file as: <bcd byte-count> ESC <text string>  
  124.    (where ESC stands for the character chr(27)). 
  125.    If the text string is a succession of sub-strings, combine them thus:
  126.       <token1> ESC <token2> ESC <token3> ESC ... 
  127.    before saving them in the file as a linked list.  Then separate them with 
  128.    'getString()' (below).
  129.    
  130. listToText(bs as binaryStream, list as integer) as string
  131.  
  132.    If 'list' points to the first block storing a text string in stream 'bs', then the 
  133.    value of that string is returned. If the list is empty, the function returns "".
  134.    
  135. getString(byref tokenStr as string) as string
  136.  
  137.    Returns the first sub-string from string 's' up to, but not including, the ESC 
  138.    character and then discards that and the ESC character from 's'. If 'tokenStr' 
  139.    is a composite string: <token1> ESC <token2> ESC ..., and has been recorded
  140.    in the file as a list pointed to by 'p', the separate tokens can be recovered thus:
  141.       s = listToText(bs, p)
  142.       t1 = getString(s)
  143.       t2 = getString(s)
  144.       ...
  145.  
  146. Using the package
  147.  
  148. Drag the icon for the package into a project. Because it is a REALbasic module, the 
  149. method names are known globally to the whole project, as are the two properties 
  150. 'numLists' and 'numSizes' if they should be needed.
  151.  
  152. Initializing a data file
  153.  
  154. At the beginning of a program should be something like this:
  155.  
  156.   initDB(5, 5)          // 5 data lists, 5 block sizes
  157.   declareBlock(1, 152)  // block type for ... 
  158.   declareBlock(2, 40)   // block type for ...
  159.   // Declare block type to be used for text stored in list form
  160.   declareTextList(1)
  161.  
  162. At present the values of the 'initDB( )' parameters 'nLists' and 'nTypes' are 
  163. recorded in the file so that, in principle, the program could read them and
  164. thereby determine how many data lists there are and how many different types of
  165. block. The programs so written so far use only one file structure and simply
  166. assume that any file that is read will have been created with the same values
  167. of these constsnts, which are stored in the globals 'numLists' aznd 'numSizes'
  168. as a result of the call to 'initDB()' when the program starts up. 
  169.   
  170. Storage and retrieval with blocks
  171.  
  172. Suppose that 'bs' is a binary stream and that we wish to store an integer, a double,
  173. and a short text string in a data block of type 2 and link it at the beginning of 
  174. data list 1. Proceed as follows:
  175.  
  176.    dim p as integer
  177.    p = newBlock(bs, 2)
  178.    addToList(bs, p, 1)
  179.    bs.position = p   // start of block's data area
  180.    bs.writeLong(myInteger)
  181.    bs.writeDouble(myDouble)
  182.    myString = left("xyz" + "          ", 10) // pad to a fixed length with spaces
  183.    bs.write(myString)
  184.  
  185. Note that the data format within the block is quite arbitrary and is simply determined
  186. by what the program writes in it.
  187.  
  188. To read from a block in this format, we first navigate to the block and then read its
  189. data:
  190.  
  191.    p = firstBlock(bs, 1)  // start of the list
  192.    // now get to the block by whatever means
  193.    // is appropriate
  194.    p = nextBlock(bs, p)
  195.    ...
  196.    // ok, we're there. now read
  197.    bs.position = p
  198.    myInteger = bs.readLong
  199.    myDouble = bs.readDouble
  200.    myString = bs.read(10)   // assuming 10-byte strings
  201.       
  202.       
  203.       
  204. Michael Maclean         e:mail: m.maclean@paradise.net.nz
  205. Christchurch
  206. New Zealand
  207.  
  208.    
  209.